/*==============================================================================
            Copyright (c) 2013 Moritz Hader, Marcel Schrder
            All Rights Reserved.

	@file  VertexManagementUnit.h

	@brief This header provides the class-definitions of the vertex-management.
	It provides functionallity to gather object-related data that are defined
	by vertex, color, normal and texture coordinates.
   
   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
==============================================================================*/
#include <egl/VertexManagementUnit.h>

#include <android/log.h>
#include <egl/HeimdallGLUtilities.h>
#include <egl/ShaderManagementUnit.h>

VertexManagementUnit::VertexManagementUnit(GLenum primType, GLuint vertices){
	this->primitive = primType;
	this->vertexCount = vertices;
	this->isExecutable = false;

	this->vertexAttribute = 0;
	this->normalAttribute = 0;
	this->colorAttribute = 0;

	this->vertexArray = NULL;
		this->vNum = 0;
	this->normalArray = NULL;
		this->nNum = 0;
	this->colorArray = NULL;
		this->cNum = 0;
}

VertexManagementUnit::~VertexManagementUnit(){
	if(vertexAttribute != 0){
		//glDeleteBuffers(1,&vertexAttribute);
	}

	if(normalAttribute != 0){
		//glDeleteBuffers(1,&normalAttribute);
	}

	if(colorAttribute != 0){
		//glDeleteBuffers(1,&colorAttribute);
	}


	//free the local arrays if necessary
	if(this->vertexArray != NULL){
		delete[] this->vertexArray;
		this->vNum = 0;
	}

	if(this->normalArray != NULL){
		delete[] this->normalArray;
		this->nNum = 0;
	}

	if(this->colorArray != NULL){
		delete[] this->colorArray;
		this->cNum = 0;
	}
}

void VertexManagementUnit::AddVertexArray(H3DVector* vertices){

	if(vertexAttribute == 0){
		//make vertex buffer
		__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Making, Binding and Initializing vertex-attribute buffer");
			glGenBuffers(1,&vertexAttribute);
			glBindBuffer(GL_ARRAY_BUFFER, vertexAttribute);
			glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*3*this->vertexCount,vertices, GL_DYNAMIC_DRAW);

			//Check if there was an error during insertion
			CheckGlError("AddVertexArray");
	}else{
		//if the buffer already exists
		glBindBuffer(GL_ARRAY_BUFFER, vertexAttribute);
		glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(GLfloat)*3*this->vertexCount,vertices);
	}
}

/**
 * Just add a single vertex to the internal array. We do this to further capsulate the mechanics
 * of this class, so that the user will not need to handle any array when parsing in vertex-data.
 */
void VertexManagementUnit::AddVertex(H3DVector vertex){

	if(this->vNum<this->vertexCount){
		if(this->vertexArray == NULL){
			this->vertexArray = (H3DVector*) malloc(sizeof(H3DVector)*this->vertexCount);
		}

		memcpy(this->vertexArray+this->vNum,vertex,sizeof(H3DVector));
		this->vNum++;
	}
	//CheckGlError("AddVertex");
}


void VertexManagementUnit::AddNormalArray(H3DVector* normals){

	if(normalAttribute == 0){
		//make vertex buffer
		__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Making, Binding and Initializing normal-attribute buffer");
			glGenBuffers(1,&normalAttribute);
			glBindBuffer(GL_ARRAY_BUFFER, normalAttribute);
			glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*3*this->vertexCount,normals, GL_DYNAMIC_DRAW);

			//Check if there was an error during insertion
			CheckGlError("AddVertexArray");
	}else{
		//if the buffer already exists
		glBindBuffer(GL_ARRAY_BUFFER, normalAttribute);
		glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(GLfloat)*3*this->vertexCount,normals);
	}
}

/**
 * Just add a single normal to the internal array. We do this to further capsulate the mechanics
 * of this class, so that the user will not need to handle any array when parsing in vertex-data.
 */
void VertexManagementUnit::AddNormal(H3DVector normal){

	if(this->nNum<this->vertexCount){
		if(this->normalArray == NULL){
			this->normalArray = (H3DVector*) malloc(sizeof(H3DVector)*this->vertexCount);
		}

		memcpy(this->normalArray+this->nNum,normal,sizeof(H3DVector));
		this->nNum++;
	}
	//CheckGlError("AddNormal");
}

void VertexManagementUnit::AddColorArray(H4DVector* color){

	if(colorAttribute == 0){
		//make vertex buffer
		__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Making, Binding and Initializing color-attribute buffer");
			glGenBuffers(1,&colorAttribute);
			glBindBuffer(GL_ARRAY_BUFFER, colorAttribute);
			glBufferData(GL_ARRAY_BUFFER,sizeof(GLfloat)*4*this->vertexCount,color, GL_DYNAMIC_DRAW);

			//Check if there was an error during insertion
			CheckGlError("AddVertexArray");
	}else{
		//if the buffer already exists
		glBindBuffer(GL_ARRAY_BUFFER, colorAttribute);
		glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(GLfloat)*4*this->vertexCount,color);
	}
}

/**
 * Just add a single vertex to the internal array. We do this to further capsulate the mechanics
 * of this class, so that the user will not need to handle any array when parsing in vertex-data.
 */
void VertexManagementUnit::AddColor(H4DVector color){

	if(this->cNum<this->vertexCount){
		if(this->colorArray == NULL){
			this->colorArray = (H4DVector*) malloc(sizeof(H4DVector)*this->vertexCount);
		}

		memcpy(this->colorArray+this->cNum,color,sizeof(H4DVector));
		this->cNum++;
	}
	//CheckGlError("AddColor");
}

void VertexManagementUnit::Finalize(){

	__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Finalizing VertexManagementUnit");

	//if vertices, normals or colors were loaded single (by local array) add them now

	if(this->vertexArray != NULL){
		AddVertexArray(this->vertexArray);
		delete[] this->vertexArray;
		this->vertexArray = NULL;
		this->vNum = 0;
	}

	if(this->normalArray != NULL){
		AddNormalArray(this->normalArray);
		delete[] this->normalArray;
		this->normalArray = NULL;
		this->nNum = 0;
	}

	if(this->colorArray != NULL){
		AddColorArray(this->colorArray);
		delete[] this->colorArray;
		this->colorArray = NULL;
		this->cNum = 0;
	}

	//Associate the index with the array, if necessary

	if(vertexAttribute != 0){ // 0 means that this array was not initialized

		//Set the given pointer as current
		glEnableVertexAttribArray(HD_ATTRIBUTES_VERTEX);

		//Set the given buffer as current
		glBindBuffer(GL_ARRAY_BUFFER, vertexAttribute);

		//Link the buffer and the pointer. The system knows now, that the source for this attribute is the given array
		glVertexAttribPointer(HD_ATTRIBUTES_VERTEX,3,GL_FLOAT,GL_FALSE,0,0);

		CheckGlError("Finalize -> vertexAttribute");
	}

	if(normalAttribute != 0){

		//Set the given pointer as current
		glEnableVertexAttribArray(HD_ATTRIBUTES_NORMAL);

		//Set the given buffer as current
		glBindBuffer(GL_ARRAY_BUFFER, normalAttribute);

		//Link the buffer and the pointer. The system knows now, that the source for this attribute is the given array
		glVertexAttribPointer(HD_ATTRIBUTES_NORMAL,3,GL_FLOAT,GL_FALSE,0,0);

		CheckGlError("Finalize -> normalAttribute");
	}

	if(colorAttribute != 0){

		//Set the given pointer as current
		glEnableVertexAttribArray(HD_ATTRIBUTES_COLOR);

		//Set the given buffer as current
		glBindBuffer(GL_ARRAY_BUFFER, colorAttribute);

		//Link the buffer and the pointer. The system knows now, that the source for this attribute is the given array
		glVertexAttribPointer(HD_ATTRIBUTES_COLOR,4,GL_FLOAT,GL_FALSE,0,0);

		CheckGlError("Finalize -> colorAttribute");
	}

	this->isExecutable = true;
}

void VertexManagementUnit::Draw(){

	//The three sections below prepare the separate buffers for drawing. This is necessary, as another instance
	//of this class may be executed before and thus the buffer-binding for a different attribute-handle
	if(this->isExecutable){

		if(vertexAttribute != 0){ // 0 means that this array was not initialized
			//Set the given pointer as current
			glEnableVertexAttribArray(HD_ATTRIBUTES_VERTEX);

			//Set the given buffer as current
			glBindBuffer(GL_ARRAY_BUFFER, vertexAttribute);

			//Link the buffer and the pointer. The system knows now, that the source for this attribute is the given array
			glVertexAttribPointer(HD_ATTRIBUTES_VERTEX,3,GL_FLOAT,GL_FALSE,0,0);
		}


		if(normalAttribute != 0){

			//Set the given pointer as current
			glEnableVertexAttribArray(HD_ATTRIBUTES_NORMAL);

			//Set the given buffer as current
			glBindBuffer(GL_ARRAY_BUFFER, normalAttribute);

			//Link the buffer and the pointer. The system knows now, that the source for this attribute is the given array
			glVertexAttribPointer(HD_ATTRIBUTES_NORMAL,3,GL_FLOAT,GL_FALSE,0,0);
		}

		if(colorAttribute != 0){

			//Set the given pointer as current
			glEnableVertexAttribArray(HD_ATTRIBUTES_COLOR);

			//Set the given buffer as current
			glBindBuffer(GL_ARRAY_BUFFER, colorAttribute);

			//Link the buffer and the pointer. The system knows now, that the source for this attribute is the given array
			glVertexAttribPointer(HD_ATTRIBUTES_COLOR,4,GL_FLOAT,GL_FALSE,0,0);
		}

		//Draw the shit
		glDrawArrays(this->primitive,0,this->vertexCount);

		//Check for errors
		CheckGlError("Draw Image");

		//Disable the array againt to allow other instances to be drawn and to prevent buffer-coruption
		glDisableVertexAttribArray(HD_ATTRIBUTES_VERTEX);
		glDisableVertexAttribArray(HD_ATTRIBUTES_NORMAL);
		glDisableVertexAttribArray(HD_ATTRIBUTES_COLOR);
	}

}

